package model
{
	import flash.display.Sprite;
	import model.observer.IModel;
	import model.observer.IView;
	import model.randomizer.MockRandomizer;
	
	import model.Shapes.*;
	import model.randomizer.ShapeRandomizer;
	import model.randomizer.SevenBagRandomizer;
	
	/**
	 * ...
	 * @author Yang Wang
	 */
	public class TetrisGame implements IModel
	{
		private var m_views:Array;
		
		private var m_hold:TetrisShape;
		private var m_performedHold:Boolean;
		
		private var m_speed:int;
		
		private var m_previewQueue:Vector.<TetrisShape>;
		private var m_previewSize:uint;
		
		private var m_board:Board;
		private var m_randomizer:ShapeRandomizer;
		private var m_scoreKeeper:ScoreKeeper;
		
		public function TetrisGame()
		{
			m_views = new Array();
			
			m_previewQueue = new Vector.<TetrisShape>();
			m_previewSize = 5;
			m_randomizer = new MockRandomizer();
			
			m_board = new Board();
			m_board.currentShape = m_randomizer.nextShape();
			
			fillPreviewQueue();
			this.updateStatus();
		}
		
		/* INTERFACE model.observer.IModel */
		
		public function addView(view:IView):void
		{
			this.m_views.push(view);
		}
		
		public function removeView(view:IView):void
		{
			m_views.splice(m_views.indexOf(view), 1);
		}
		
		public function notifyViews():void
		{
			for (var i:int = 0; i < m_views.length; i++)
			{
				var view:IView = m_views[i] as IView;
				view.updateView(this);
			}
		}
		
		private function fillPreviewQueue():void
		{
			while (m_previewQueue.length < m_previewSize)
			{
				m_previewQueue.push(m_randomizer.nextShape());
			}
		}
		
		public function nextShape():TetrisShape
		{
			var shape:TetrisShape = this.previewQueue.shift();
			fillPreviewQueue();
			return shape;
		}
		
		public function get previewSize():uint
		{
			return m_previewSize;
		}
		
		public function get previewQueue():Vector.<TetrisShape>
		{
			return m_previewQueue;
		}
		
		public function get hold():TetrisShape
		{
			return m_hold;
		}
		
		public function get board():Board
		{
			return m_board;
		}
		
		public function set board(value:Board):void
		{
			m_board = value;
		}
		
		public function get currentShape():TetrisShape
		{
			return m_board.currentShape;
		}
		
		public function get currentShapePosition():Point
		{
			return m_board.shapePosition;
		}
		
		public function updateStatus():void
		{
			holdPerformed = false;
			
			if (m_board.currentShape == null)
			{
				var shape:TetrisShape = this.nextShape();
				if (m_board.canAddNewShape(shape))
				{
					m_board.currentShape = shape;
					fillPreviewQueue();
				}
				else
				{
    					throw new Error("GAME OVER!");
				}
			}
			
			notifyViews();
		}
		
		public function set currentShape(shape:TetrisShape):void
		{
			this.m_board.currentShape = shape;
		}
		
		public function holdCurrentShape():void
		{
			if (holdPerformed)
				return;
			
			if (m_hold == null)
			{
				m_hold = this.currentShape;
				this.currentShape = this.nextShape();
			}
			else
			{
				var tetrisShape:TetrisShape = m_hold;
				m_hold = this.currentShape;
				m_hold.resetOrientation();
				this.currentShape = tetrisShape;
			}
			
			holdPerformed = true;
			notifyViews();
		}
		
		public function get holdPerformed():Boolean
		{
			return this.m_performedHold;
		}
		
		public function set holdPerformed(performed:Boolean):void
		{
			m_performedHold = performed;
		}
	}

}